// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件实现类型打印。

package types2

import (
	"bytes"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"unicode/utf8"
)

// 限定符控制命名包级别对象在
// 调用TypeString、ObjectString和SelectionString中的打印方式。
// 
// 这三个格式化例程为每个
// 包级对象O调用限定符，如果限定符返回非空的
// 字符串p，则该对象以p.O的形式打印。
// 如果返回空字符串，则只打印对象名O。
// 
// 使用nil限定符等同于使用（*Package）。路径：
// 对象由导入路径限定，例如“encoding/json.Marshal”。
// 
type Qualifier func(*Package) string

// Relative返回一个限定符，该限定符完全限定
// 除pkg之外的所有包的成员。
func RelativeTo(pkg *Package) Qualifier {
	if pkg == nil {
		return nil
	}
	return func(other *Package) string {
		if pkg == other {
			return "" // 同一包装；unqualified 
		}
		return other.Path()
	}
}

// TypeString返回typ的字符串表示形式。
// 限定符控制打印
// 包级对象，可以为零。
func TypeString(typ Type, qf Qualifier) string {
	return typeString(typ, qf, false)
}

func typeString(typ Type, qf Qualifier, debug bool) string {
	var buf bytes.Buffer
	w := newTypeWriter(&buf, qf)
	w.debug = debug
	w.typ(typ)
	return buf.String()
}

// WriteType将typ的字符串表示形式写入buf。
// 限定符控制打印
// 包级对象，可以为零。
func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
	newTypeWriter(buf, qf).typ(typ)
}

// WriteSignature将签名sig的表示形式写入buf，
// 不带前导的“func”关键字。
// 限定符控制打印
// 包级对象，可以为零。
func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
	newTypeWriter(buf, qf).signature(sig)
}

type typeWriter struct {
	buf     *bytes.Buffer
	seen    map[Type]bool
	qf      Qualifier
	ctxt    *Context       // 如果非nil，我们是类型散列
	tparams *TypeParamList // 本地类型参数
	debug   bool           // 如果为true，编写调试注释
}

func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
	return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, false}
}

func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
	assert(ctxt != nil)
	return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false}
}

func (w *typeWriter) byte(b byte) {
	if w.ctxt != nil {
		if b == ' ' {
			b = '#'
		}
		w.buf.WriteByte(b)
		return
	}
	w.buf.WriteByte(b)
	if b == ',' || b == ';' {
		w.buf.WriteByte(' ')
	}
}

func (w *typeWriter) string(s string) {
	w.buf.WriteString(s)
}

func (w *typeWriter) error(msg string) {
	if w.ctxt != nil {
		panic(msg)
	}
	w.buf.WriteString("<" + msg + ">")
}

func (w *typeWriter) typ(typ Type) {
	if w.seen[typ] {
		w.error("cycle to " + goTypeName(typ))
		return
	}
	w.seen[typ] = true
	defer delete(w.seen, typ)

	switch t := typ.(type) {
	case nil:
		w.error("nil")

	case *Basic:
		// 导出的基本类型进入包不安全
		// （目前这只是不安全的指针）
		if isExported(t.name) {
			if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
				w.typeName(obj)
				break
			}
		}
		w.string(t.name)

	case *Array:
		w.byte('[')
		w.string(strconv.FormatInt(t.len, 10))
		w.byte(']')
		w.typ(t.elem)

	case *Slice:
		w.string("[]")
		w.typ(t.elem)

	case *Struct:
		w.string("struct{")
		for i, f := range t.fields {
			if i > 0 {
				w.byte(';')
			}
			// 这对嵌入类型
			// 别名不正确，我们应该在其中打印别名，而不是
			// 别名类型（见问题#44410）。
			if !f.embedded {
				w.string(f.name)
				w.byte(' ')
			}
			w.typ(f.typ)
			if tag := t.Tag(i); tag != "" {
				w.byte(' ')
				// TODO（gri）如果标记包含空格，则在上下文中将其替换为“#”
				// 。TypeHash可能会意外地生成另一个标记
				// 。
				w.string(strconv.Quote(tag))
			}
		}
		w.byte('}')

	case *Pointer:
		w.byte('*')
		w.typ(t.base)

	case *Tuple:
		w.tuple(t, false)

	case *Signature:
		w.string("func")
		w.signature(t)

	case *Union:
		// 联合仅作为（语法）嵌入元素出现在接口中，并且在语法上不能为空。
		if t.Len() == 0 {
			w.error("empty union")
			break
		}
		for i, t := range t.terms {
			if i > 0 {
				w.byte('|')
			}
			if t.tilde {
				w.byte('~')
			}
			w.typ(t.typ)
		}

	case *Interface:
		if w.ctxt == nil {
			if t == universeAny.Type() {
				// 当不进行哈希运算时，我们可以尝试通过为一个与universeAny指针相同的类型编写“any”
				// 来改进类型字符串。
				// 这种逻辑应该被更健壮的别名处理所否决。
				w.string("any")
				break
			}
			if t == universeComparable.Type().(*Named).underlying {
				w.string("interface{comparable}")
				break
			}
		}
		if t.implicit {
			if len(t.methods) == 0 && len(t.embeddeds) == 1 {
				w.typ(t.embeddeds[0])
				break
			}
			// 隐式接口有问题。
			// 打印并继续。
			w.string("/* implicit */ ")
		}
		w.string("interface{")
		first := true
		if w.ctxt != nil {
			w.typeSet(t.typeSet())
		} else {
			for _, m := range t.methods {
				if !first {
					w.byte(';')
				}
				first = false
				w.string(m.name)
				w.signature(m.typ.(*Signature))
			}
			for _, typ := range t.embeddeds {
				if !first {
					w.byte(';')
				}
				first = false
				w.typ(typ)
			}
		}
		w.byte('}')

	case *Map:
		w.string("map[")
		w.typ(t.key)
		w.byte(']')
		w.typ(t.elem)

	case *Chan:
		var s string
		var parens bool
		switch t.dir {
		case SendRecv:
			s = "chan "
			// chan（<-chan T）需要括号
			if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
				parens = true
			}
		case SendOnly:
			s = "chan<- "
		case RecvOnly:
			s = "<-chan "
		default:
			w.error("unknown channel direction")
		}
		w.string(s)
		if parens {
			w.byte('(')
		}
		w.typ(t.elem)
		if parens {
			w.byte(')')
		}

	case *Named:
		// 如果进行哈希运算，请为T写一个唯一的前缀来表示其标识，因为
		// 命名类型标识是指针标识。
		if w.ctxt != nil {
			w.string(strconv.Itoa(w.ctxt.getID(t)))
		}
		w.typeName(t.obj) // 当仅为哈希的可读性而编写哈希时
		if t.targs != nil {
			// 实例化类型
			w.typeList(t.targs.list())
		} else if w.ctxt == nil && t.TypeParams().Len() != 0 { // 对于类型哈希，不需要格式化类型参数
			// 参数化类型
			w.tParamList(t.TypeParams().list())
		}

	case *TypeParam:
		if t.obj == nil {
			w.error("unnamed type parameter")
			break
		}
		if i := tparamIndex(w.tparams.list(), t); i >= 0 {
			// 被
			// 哈希的类型声明的类型参数的名称不属于类型标识。将它们替换为表示其索引的
			// 占位符。
			w.string(fmt.Sprintf("$%d", i))
		} else {
			w.string(t.obj.name)
			if w.debug || w.ctxt != nil {
				w.string(subscript(t.id))
			}
		}

	default:
		// 用于类型的外部定义实现。
		// 注意：在这种情况下，不会捕获周期。
		w.string(t.String())
	}
}

// typeSet为接口类型集写入规范哈希。
func (w *typeWriter) typeSet(s *_TypeSet) {
	assert(w.ctxt != nil)
	first := true
	for _, m := range s.methods {
		if !first {
			w.byte(';')
		}
		first = false
		w.string(m.name)
		w.signature(m.typ.(*Signature))
	}
	switch {
	case s.terms.isAll():
		// 没什么可做的
	case s.terms.isEmpty():
		w.string(s.terms.String())
	default:
		var termHashes []string
		for _, term := range s.terms {
			// 术语不是按规范排序的，所以我们对它们的哈希进行排序。
			var buf bytes.Buffer
			if term.tilde {
				buf.WriteByte('~')
			}
			newTypeHasher(&buf, w.ctxt).typ(term.typ)
			termHashes = append(termHashes, buf.String())
		}
		sort.Strings(termHashes)
		if !first {
			w.byte(';')
		}
		w.string(strings.Join(termHashes, "|"))
	}
}

func (w *typeWriter) typeList(list []Type) {
	w.byte('[')
	for i, typ := range list {
		if i > 0 {
			w.byte(',')
		}
		w.typ(typ)
	}
	w.byte(']')
}

func (w *typeWriter) tParamList(list []*TypeParam) {
	w.byte('[')
	var prev Type
	for i, tpar := range list {
		// 确定类型参数及其约束。
		// 列表应该包含类型参数名，
		// 但如果不是这样，请不要崩溃。
		if tpar == nil {
			w.error("nil type parameter")
			continue
		}
		if i > 0 {
			if tpar.bound != prev {
				// 绑定已更改-在前进之前写入上一个
				w.byte(' ')
				w.typ(prev)
			}
			w.byte(',')
		}
		prev = tpar.bound
		w.typ(tpar)
	}
	if prev != nil {
		w.byte(' ')
		w.typ(prev)
	}
	w.byte(']')
}

func (w *typeWriter) typeName(obj *TypeName) {
	if obj.pkg != nil {
		writePackage(w.buf, obj.pkg, w.qf)
	}
	w.string(obj.name)
}

func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
	w.byte('(')
	if tup != nil {
		for i, v := range tup.vars {
			if i > 0 {
				w.byte(',')
			}
			// 类型标识忽略参数名称，因此类型哈希
			if w.ctxt == nil && v.name != "" {
				w.string(v.name)
				w.byte(' ')
			}
			typ := v.typ
			if variadic && i == len(tup.vars)-1 {
				if s, ok := typ.(*Slice); ok {
					w.string("...")
					typ = s.elem
				} else {
					// 特殊情况：
					// 追加（s，“foo”…）导致签名函数（[]字节，字符串…）wen jian efg
					if t, _ := under(typ).(*Basic); t == nil || t.kind != String {
						w.error("expected string type")
						continue
					}
					w.typ(typ)
					w.string("...")
					continue
				}
			}
			w.typ(typ)
		}
	}
	w.byte(')')
}

func (w *typeWriter) signature(sig *Signature) {
	if sig.TypeParams().Len() != 0 {
		if w.ctxt != nil {
			assert(w.tparams == nil)
			w.tparams = sig.TypeParams()
			defer func() {
				w.tparams = nil
			}()
		}
		w.tParamList(sig.TypeParams().list())
	}

	w.tuple(sig.params, sig.variadic)

	n := sig.results.Len()
	if n == 0 {
		return
	}

	w.byte(' ')
	if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
		w.typ(sig.results.vars[0].typ)
		return
	}

	w.tuple(sig.results, false)
}

func subscript(x uint64) string {
	const w = len("₀") // 所有数字0。。。9具有相同的utf8宽度
	var buf [32 * w]byte
	i := len(buf)
	for {
		i -= w
		utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // /'₀' == U+2080 
		x /= 10
		if x == 0 {
			break
		}
	}
	return string(buf[i:])
}
